﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Game
{
    class Map
    {
        int _width, _height;
        const int XHP = 99999;
        char[,] _map;
        int[,] _aMap;
        bool[,] _mask;
        Position _aBEST;
        List<Slime> _PlayerOne;
        List<Slime> _PlayerTwo;
        List<Position> _PosList;

        public Map(int width = 36, int height = 8)
        {
            _width = width;
            _height = height;
            _map = new char[_height, _width];
            _mask = new bool[_height, _width];
            _aMap = new int[_height, _width];
            _aBEST = new Position();
            _PosList = new List<Position>();
        }
        public Map(Map other)
        {
            _width = other._width;
            _height = other._height;
            _map = (char[,])other._map.Clone();
            _mask = new bool[_height, _width];
            _aMap = new int[_height, _width];
            _aBEST = new Position();
            _PosList = new List<Position>();
        }
        public int Width
        {
            get { return _width; }
        }
        public int Height
        {
            get { return _height; }
        }

        public void Display()
        {
            Console.Write("   "); for (int i = 0; i < _width; i++) { Console.Write(i % 10 + " "); } Console.Write("\n");
            Console.Write("   "); for (int i = 0; i < 2 * _width; i++) { Console.Write("*"); } Console.Write("\n");
            for (int i = 0; i < _height; i++)
            {
                Console.Write(i + " *");
                for (int j = 0; j < _width; j++)
                {
                    Console.Write(_map[i, j] + " ");
                }
                Console.Write("*\n");
            }
            Console.Write("   "); for (int i = 0; i < 2 * _width; i++) { Console.Write("*"); } Console.Write("\n");
        }

        public void Debug()
        {
            
            Console.Write("   "); for (int i = 0; i < _width; i++) { Console.Write(i % 10 + " "); } Console.Write("\n");
            Console.Write("   "); for (int i = 0; i < 2 * _width; i++) { Console.Write("*"); } Console.Write("\n");
            for (int i = 0; i < _height; i++)
            {
                Console.Write(i + " *");
                for (int j = 0; j < _width; j++)
                {

                    Console.Write(_aMap[i, j].ToString("D2") + "  ");
                }
                Console.Write("*\n");
            }
            Console.Write("   "); for (int i = 0; i < 2 * _width; i++) { Console.Write("*"); } Console.Write("\n");
        }
        public void DebugM()
        {
            System.Console.Clear();
            Display();
            Console.Write("   "); for (int i = 0; i < _width; i++) { Console.Write(i % 10 + " "); } Console.Write("\n");
            Console.Write("   "); for (int i = 0; i < 2 * _width; i++) { Console.Write("*"); } Console.Write("\n");
            for (int i = 0; i < _height; i++)
            {
                Console.Write(i + " *");
                for (int j = 0; j < _width; j++)
                {
                    int ret = 0;
                    if (_mask[i, j]) ret = 1;
                    Console.Write(ret + " ");
                }
                Console.Write("*\n");
            }
            Console.Write("   "); for (int i = 0; i < 2 * _width; i++) { Console.Write("*"); } Console.Write("\n");
        }

        public int DFS(Position P, int dis,char side){
            _PosList.Clear();
            _PosList.Add(P);
            _mask[P.Y, P.X] = true;
            if (char.IsUpper(side))
            {
               return  DFSp2(P, dis);
            }
            else if (char.IsLower(side))
            {
                return DFSp1(P, dis);
            }
            return -99999;
        }
        public int DFSp1(Position P, int dis)
        {
            if (dis == -1) return 0;
            if (P.X < 0 || P.X >= _width) return 0;
            if (P.Y < 0 || P.Y >= _height) return 0;
            if (_map[P.Y, P.X] == ' ' || char.IsLower(_map[P.Y, P.X]))
            {
                if (_map[P.Y, P.X] == ' ' && !_mask[P.Y, P.X])
                {
                    _mask[P.Y, P.X] = true;
                    _PosList.Add(new Position(P.X,P.Y));
                }
                DFSp1(new Position(P.X + 1, P.Y), dis - 1);
                DFSp1(new Position(P.X - 1, P.Y), dis - 1);
                DFSp1(new Position(P.X, P.Y + 1), dis - 1);
                DFSp1(new Position(P.X, P.Y - 1), dis - 1);
            }
            return 0;
        }

        public int DFSp2(Position P, int dis)
        {
            if (dis == -1) return 0;
            if (P.X < 0 || P.X >= _width) return 0;
            if (P.Y < 0 || P.Y >= _height) return 0;
            if (_map[P.Y, P.X] == ' ' || char.IsUpper(_map[P.Y, P.X]))
            {
                if (_map[P.Y, P.X] == ' ' && !_mask[P.Y, P.X])
                {
                    _mask[P.Y, P.X] = true;
                    _PosList.Add(new Position(P.X, P.Y));
                }
                DFSp2(new Position(P.X + 1, P.Y), dis - 1);
                DFSp2(new Position(P.X - 1, P.Y), dis - 1);
                DFSp2(new Position(P.X, P.Y + 1), dis - 1);
                DFSp2(new Position(P.X, P.Y - 1), dis - 1);
            }
            return 0;
        }

        public void SetMap(int x, int y, char c)
        {
            _map[y, x] = c;
        }
        void Clear()
        {
            for (int i = 0; i < _height; i++)
            {
                for (int j = 0; j < _width; j++)
                {
                    _map[i, j] = ' ';
                }
            }
        }
        void ClearMask()
        {
            for (int i = 0; i < _height; i++)
            {
                for (int j = 0; j < _width; j++)
                {
                    _mask[i, j] = false;
                }
            }
        }
        void ClearAMap()
        {
            for (int i = 0; i < _height; i++)
            {
                for (int j = 0; j < _width; j++)
                {
                    _aMap[i, j] = 0;
                }
            }
        }

        public bool CanGOp1(Position P, int dis, Position G)
        {
            ClearMask();
            DFSp1(P, dis);
            //DebugM();
            return _mask[G.Y, G.X];
        }

        public bool CanGOp2(Position P, int dis, Position G)
        {
            ClearMask();
            DFSp2(P, dis);
            //DebugM();
            return _mask[G.Y, G.X];
        }

        public void Update(Player P1, Player P2)
        {
            Clear();
            _PlayerOne = P1.Slimes;
            _PlayerTwo = P2.Slimes;
            for (int i = 0; i < _PlayerOne.Count; i++)
            {
                if (_PlayerOne[i].HP > 0)
                {
                    _map[_PlayerOne[i].Y, _PlayerOne[i].X] = _PlayerOne[i].Icon;
                }
            }
            for (int i = 0; i < _PlayerTwo.Count; i++)
            {
                if (_PlayerTwo[i].HP > 0)
                {
                    _map[_PlayerTwo[i].Y, _PlayerTwo[i].X] = char.ToUpper(_PlayerTwo[i].Icon);
                }
            }
        }

        public bool IsSpace(int x, int y)
        {
            Position P = new Position(x, y);
            return IsSpace(P);
        }
        public bool IsSpace(Position P)
        {

            if (0 <= P.X && P.X < _width && 0 <= P.Y && P.Y < _height)
            {
                return (_map[P.Y, P.X] == ' ');
            }
            return false;
        }
        public Position Chase(Position A, Position B, int dis, int rag, char side)
        {
            Position ret = new Position(99,99);
            ClearMask();
            DFS(A, dis,side);
            
            //DebugM();
            // 如果可以攻擊到目標 回傳位置P
            for (int i = -1 * rag; i <= rag; i++)
            {
                for (int j = rag - Math.Abs(i); j >= -1 * (rag - Math.Abs(i)); j--)
                {
                    if (B.Y - i < 0 || B.Y - i >= _height || B.X - j < 0 || B.X - j >= _width)
                    {
                        continue;
                    }
                    if (_mask[B.Y - i, B.X - j])
                    {
                        ret.X = B.X - j;
                        ret.Y = B.Y - i;
                        return ret;
                    }
                }
            }
            // 如果無法攻擊到目標 使用A * 回傳有最靠近的的位置P
            int shortest = 999;
            for (int i = -1 * dis; i <= dis; i++)
            {
                for (int j = dis - Math.Abs(i); j >= -1 * (dis - Math.Abs(i)); j--)
                {
                    Position P = new Position(A.X - j, A.Y - i);
                    if (!Isin(P) || !_mask[P.Y,P.X] )
                    {
                        continue;
                    }
                    int temp = AStar(P, B, dis, side);
                    if (temp < shortest || (temp == shortest && Distance(P, B) < Distance(ret, B)))
                    {
                        shortest = temp;
                        ret = P;
                    }
                }
            }
            if (ret.X == 99 && ret.Y == 99) { ret = A;}
            //DebugM();
            //Debug();
            return ret;
        }
        public int AStar(Position A, Position B, int dis,char side)
        {
            ClearAMap();
            _aMap[A.Y, A.X] = Distance(A, B);
            _aBEST = A;
            return DoAStar(A, B, dis, side);
        }
        public int DoAStar(Position A, Position B, int dis, char side)
        {
            const int NOTSPACE = 99;
            int g = 0;
            List<Position> L = new List<Position>();
            List<int> Lg = new List<int>();
            while (Distance(A, B) > 1)
            {
                
                Position P;
                //Debug();
                if (A.X + 1 < _width)
                {
                    P = new Position(A.X + 1, A.Y);
                    if ((_map[P.Y, P.X] == ' ' ||  !(char.IsUpper(_map[P.Y, P.X])^char.IsUpper(side))) //是隊友
                        && _aMap[P.Y, P.X] == 0)
                    {
                        if (_map[P.Y, P.X] == ' ') { _aMap[P.Y, P.X] = g + 1 + Distance(P, B); }
                        else { _aMap[P.Y, P.X] = NOTSPACE; }
                        L.Add(new Position(P.X, P.Y)); Lg.Add(g);
                        if (_aMap[P.Y, P.X] <= _aMap[_aBEST.Y, _aBEST.X] && Distance(P, B) < Distance(_aBEST,B))
                        {
                            _aBEST = P;
                        }
                    }

                }
                //Debug();
                if (A.X - 1 >= 0)
                {
                    P = new Position(A.X - 1, A.Y);
                    if ((_map[P.Y, P.X] == ' ' || !(char.IsUpper(_map[P.Y, P.X]) ^ char.IsUpper(side))) //是隊友
                         && _aMap[P.Y, P.X] == 0)
                    {
                        if (_map[P.Y, P.X] == ' ') { _aMap[P.Y, P.X] = g + 1 + Distance(P, B); }
                        else { _aMap[P.Y, P.X] = NOTSPACE; }
                        L.Add(new Position(P.X, P.Y)); Lg.Add(g);
                        if (_aMap[P.Y, P.X] <= _aMap[_aBEST.Y, _aBEST.X] && Distance(P, B) < Distance(_aBEST, B))
                        {
                            _aBEST = P;
                        }
                    }
                }
                //Debug();
                if (A.Y + 1 < _height)
                {
                    P = new Position(A.X, A.Y + 1);
                    if ((_map[P.Y, P.X] == ' ' || !(char.IsUpper(_map[P.Y, P.X]) ^ char.IsUpper(side))) //是隊友
                        && _aMap[P.Y, P.X] == 0)
                    {
                        if (_map[P.Y, P.X] == ' ') { _aMap[P.Y, P.X] = g + 1 + Distance(P, B); }
                        else { _aMap[P.Y, P.X] = NOTSPACE; }
                        L.Add(new Position(P.X, P.Y)); Lg.Add(g);
                        if (_aMap[P.Y, P.X] <= _aMap[_aBEST.Y, _aBEST.X] && Distance(P, B) < Distance(_aBEST, B))
                        {
                            _aBEST = P;
                        }
                    }

                }
                //Debug();
                if (A.Y - 1 >= 0)
                {
                    P = new Position(A.X, A.Y - 1);
                    if ((_map[P.Y, P.X] == ' ' || !(char.IsUpper(_map[P.Y, P.X]) ^ char.IsUpper(side))) //是隊友
                        && _aMap[P.Y, P.X] == 0)
                    {
                        if (_map[P.Y, P.X] == ' ') { _aMap[P.Y, P.X] = g + 1 + Distance(P, B); }
                        else { _aMap[P.Y, P.X] = NOTSPACE; }
                        L.Add(new Position(P.X, P.Y)); Lg.Add(g);
                        if (_aMap[P.Y, P.X] <= _aMap[_aBEST.Y, _aBEST.X] && Distance(P, B) < Distance(_aBEST, B))
                        {
                            _aBEST = P;
                        }
                    }

                }
               // Debug();

                if (L.Count == 0) {break; }
                P = L[0];
                int tg = Lg[0];
                int min = _aMap[P.Y, P.X];
                for (int i = 1; i < L.Count; i++)
                {
                    int temp = _aMap[L[i].Y, L[i].X];
                    if (temp < min)
                    {
                        min = temp;
                        P = L[i];
                        tg = Lg[i];
                    }
                }

                A = P;
                g = tg;
                L.Remove(P);
                Lg.Remove(tg);
                ++g;
                //Debug();
            }//end while
            
            return g+1;

        }

        public int Distance(Position A, Position B)
        {
            return Math.Abs(A.X - B.X) + Math.Abs(A.Y - B.Y);
        }
        public void Fix(ref Position P)
        {
            if (P.X >= _width) { P.X = _width - 1; }
            else if (P.X < 0) { P.X = 0; }
            if (P.Y >= _height) { P.Y = _height - 1; }
            else if (P.Y < 0) { P.Y = 0; }
        }
        public bool Isin(Position P)
        {
            if (P.X >= _width) { return false; }
            else if (P.X < 0) { return false; }
            if (P.Y >= _height) { return false; }
            else if (P.Y < 0) { return false; }
            return true;
        }
        public int Round(Position P,char side)
        {
            int ret = 0;
            bool sw = false;
            if (side == 'a') {
                sw = true;
            }
            if(sw){
                for (int i = 0; i < _PlayerTwo.Count; i++) {
                    if (Distance(_PlayerTwo[i].Pos, P) == 1)
                    {
                        ++ret;
                    }
                }
            }else{
                for (int i = 0; i < _PlayerOne.Count; i++)
                {
                    if (Distance(_PlayerOne[i].Pos, P) == 1)
                    {
                        ++ret;
                    }
                }
            }
            return ret;
        }

        public Position Search(Slime S)
        {
            List<Position> ListHP = new List<Position>(); //特別用途
            ClearMask();
            DFS(S.Pos,S.MovDis,S.Side);
            for(int i =0;i<_PosList.Count;i++){
                int HP = HaveEnmey(_PosList[i],S.Range,S.Side);
                if( HP != XHP){
                    ListHP.Add(new Position(i,HP));
                }else{
                    _PosList.RemoveAt(i);
                    i--;
                }
            }
            ListHP.Sort((a,b)=> {return (a.Y.CompareTo(b.Y));});
            Position ret;
            if (ListHP.Count > 0)
            {
                ret = _PosList[ListHP[0].X];
            }else{
                ret = new Position (-1,-1);
            }
            return ret;

        }
        int HaveEnmey(Position P,int dis,char side){
            int ret = XHP;

            if (char.IsUpper(side)) {
                for (int i = 0; i < _PlayerOne.Count; i++)
                {
                    Slime tempS = _PlayerOne[i];
                    if (tempS.HP <= 0)
                    {
                        continue;
                    }
                    if (Math.Abs(P.X - tempS.X) + Math.Abs(P.Y - tempS.Y) <= dis && tempS.HP<ret)
                    {
                        ret = tempS.HP;
                    }
                    
                }
            }
            else if (char.IsLower(side))
            {
                for (int i = 0; i < _PlayerTwo.Count; i++)
                {
                    Slime tempS = _PlayerTwo[i];
                    if (tempS.HP <= 0)
                    {
                        continue;
                    }
                    if (Math.Abs(P.X - tempS.X) + Math.Abs(P.Y - tempS.Y) <= dis && tempS.HP < ret)
                    {
                        ret = tempS.HP;
                    }
                }
            }
            return ret;
        }

    }
}
